Skip to content

bake: provide every runtime helper from the dev server's bun:wrap module - #44688

Open
robobun wants to merge 2 commits into
mainfrom
robobun/ff97d80a/hmr-bun-wrap-all-helpers
Open

robobun wants to merge 2 commits into
mainfrom
robobun/ff97d80a/hmr-bun-wrap-all-helpers

Conversation

@robobun

@robobun robobun commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #44463

Problem

  • A TC39-decorated class fails in the dev server, client and server side: TypeError: import_bun_wrap.__decoratorStart is not a function. bun build handles the same file.
  • The lowered class calls __decoratorStart, __decorateElement, and the __private* helpers through hmr.require("bun:wrap"). The HMR runtime built that module from a hand-written list of eight helpers (src/runtime/bake/hmr-module.ts:910).

Fix

  • hmr-module.ts registers the whole ../../runtime.bun namespace as bun:wrap (32 keys, was 8). The list cannot drift again. The spread keeps data properties, so a helper read stays one getter hop. The dead declare module "bun:wrap" block goes away.
  • scripts/glob-sources.ts adds src/runtime.js and src/runtime.bun.js to the inputs of the bake codegen step, so a helper change regenerates bake.client.js and bake.server.js.
  • Verified: test/bake/dev/bundle.test.ts, two new tests (client and server, with a hot update), both fail on main. Also ran test/bake/dev-and-prod.test.ts, dev/esm.test.ts, dev/hot.test.ts. Self-reviewed: 3 concerns, 3 addressed.

Background

Downsides

  • The dev runtimes grow: bake.client.js +4708 bytes raw (+1699 zstd -19), bake.server.js +4682 raw (+1691 zstd -19), bake.error.js +0 (bake-codegen, main vs this branch). The bytes sit in the binary and go out once per page load in development.
  • Helpers the dev server never emits (__commonJS, __toESM, __esm) are reachable through bun:wrap, the same as under bun build.
Notes
  • Imports::ALL coverage after the fix: 26 of 27 names. The missing one is __require. runtime.js does not export it and the dev server rewrites it to hmr.require (pinned by test/bake/dev-and-prod.test.ts).
  • Bundled shape (from bake.server.js): fe("bun:wrap",{...te}) where te is the __export(runtime_exports, {...}) namespace with 32 getters. The spread runs once at runtime load.
  • hmr-runtime-error.ts does not import hmr-module.ts, so bake.error.js is unchanged.
  • The test fixture is the reporter's, from the issue. The credit is in the commit trailer.
  • Pre-existing, out of scope: the browser client receives the Bun variant of __using and __callDispose from runtime.bun.js.

robobun and others added 2 commits October 7, 2026 07:34
The HMR runtime registered the synthetic bun:wrap module from a hand-written
list of eight helpers. The transpiler also emits the TC39 decorator helpers
and the private member helpers through bun:wrap, so a decorated class threw
"import_bun_wrap.__decoratorStart is not a function" in the dev server.

Register the whole runtime.bun namespace instead. The spread keeps the
exports as data properties, so a helper read stays one getter hop. Delete the
declare module block that nothing imports.

Co-authored-by: TiedWithType <TiedWithType@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: oven-sh/bun/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: 03eea966-7a29-422c-82da-c6959fd67a43
📥 Commits

Reviewing files that changed from the base of the PR and between bd599f5 and 72ee739.

📒 Files selected for processing (4)
  • scripts/glob-sources.ts
  • src/runtime/bake/bake.private.d.ts
  • src/runtime/bake/hmr-module.ts
  • test/bake/dev/bundle.test.ts
💤 Files with no reviewable changes (1)
  • src/runtime/bake/bake.private.d.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.


Walkthrough

The bake runtime source list now includes additional runtime files. The HMR runtime registers exports from runtime.bun in the synthetic bun:wrap module. New client and server tests cover decorated modules before and after HMR updates.

Changes

TC39 Decorators in HMR

Layer / File(s) Summary
Runtime helper registration
scripts/glob-sources.ts, src/runtime/bake/bake.private.d.ts, src/runtime/bake/hmr-module.ts
The bake runtime source list includes src/runtime.js and src/runtime.bun.js. The HMR module imports runtime helpers as a namespace and registers its exports in bun:wrap. The previous bun:wrap declaration is removed.
Decorator HMR coverage
test/bake/dev/bundle.test.ts
Client and server tests exercise decorated fields, accessors, methods, private members, metadata, and initializers. Both tests check output before and after a field initializer update.

Suggested reviewers: dylan-conway

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 72ee7

This change makes all runtime helpers available to decorated modules under HMR. No concrete merge-blocking risk was found.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed #44463 requires TC39-decorated modules to work in client and server HMR and after updates. hmr-module.ts registers the runtime.bun namespace as bun:wrap, which makes the emitted decorator and pr…
Out of Scope Changes check ✅ Passed All changes support #44463. The runtime source inputs in glob-sources.ts keep generated bake runtimes current when helpers change. Removing the obsolete bun:wrap declaration block and adding regre…
Title check ✅ Passed The title clearly identifies the main change: providing all runtime helpers through the dev server's synthetic bun:wrap module.
Description check ✅ Passed The description explains the problem, the fix, and how the author verified it. It does not use the template's exact headings, but it provides the requested information in equivalent sections.
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment thread scripts/glob-sources.ts
Comment on lines +68 to +69
"src/runtime.js",
"src/runtime.bun.js",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 pre-existing, not blocking: Pre-existing sibling of this fix: a developer who edits src/runtime.js still gets a stale bun run helper module after bun bd, because only the bake bundles now track it. The runtime.out.js step in scripts/build/codegen.ts:504-525 lists only src/runtime.bun.js as its input, and the esbuild rule (scripts/build/codegen.ts:144) has no depfile, so an edit to runtime.js (which runtime.bun.js re-exports) never re-runs it. Fix: make every consumer of runtime.js rebuild when it changes, i.e. add resolve(cfg.cwd, "src", "runtime.js") to the inputs of emitRuntimeJs alongside the glob-sources change here, so the bun:wrap module served by bun run (src/runtime/jsc_hooks.rs:3599) is regenerated too.
A small fix can ride a push you are already making; otherwise a short reply is enough.

Why this was flagged

A developer changes a helper in src/runtime.js and runs an incremental build. scripts/glob-sources.ts:68-69 now adds src/runtime.js and src/runtime.bun.js to bakeRuntime, so bake.client.js and bake.server.js are regenerated. But emitRuntimeJs at scripts/build/codegen.ts:500-529 declares inputs: [src] where src is only src/runtime.bun.js, and the "esbuild" rule at scripts/build/codegen.ts:144-147 declares no depfile, so ninja does not know runtime.out.js depends on src/runtime.js. runtime.out.js is what src/ast/runtime.rs:21 embeds and what src/runtime/jsc_hooks.rs:3595-3602 serves as the bun:wrap module for bun run. The result is a binary whose dev-server helpers are current but whose bun run helpers are the previous version; tests for bun run behaviour pass or fail against stale code until a clean build. The base branch has the same gap for runtime.out.js and additionally had it for the bake bundles; this PR closes only the bake half of the class.

Verification: src/runtime.bun.js:1 is export * from "./runtime";, so runtime.out.js is a bundle of runtime.js content. scripts/build/codegen.ts:500-509 declares inputs: [src] only, and the esbuild rule at scripts/build/codegen.ts:144-147 sets no depfile/deps. So ninja has no edge from src/runtime.js to runtime.out.js. The base branch already fails the same way for runtime.out.js.

__name,
__using,
} from "../../runtime.bun";
import * as runtimeHelpers from "../../runtime.bun";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 pre-existing, not blocking: Browser users of the dev server on engines without SuppressedError get a ReferenceError from __callDispose when a using disposer throws, instead of the synthetic SuppressedError the production build gives. The namespace import at hmr-module.ts:9 pulls __callDispose from src/runtime.bun.js:19-22, which calls new SuppressedError(...) unconditionally; the browser variant at src/bundler/ParseTask.rs:533-534 has a fallback that the client HMR runtime never receives. Fix: build the client bun:wrap from the browser runtime variant (or add the typeof SuppressedError === 'function' fallback to runtime.bun.js's __callDispose) so client and bun build --target browser behave the same.
A small fix can ride a push you are already making; otherwise a short reply is enough.

Why this was flagged

If a dev-server client runs in a browser that lacks SuppressedError, a module using using/await using whose disposer throws reaches __callDispose at src/runtime.bun.js:19-23, which does new SuppressedError(e, error, ...) and throws a ReferenceError instead. The production bundler for --target browser uses RUNTIME_USING_OTHER at src/bundler/ParseTask.rs:514-549, whose __callDispose at ParseTask.rs:533-534 checks typeof SuppressedError === 'function' and falls back to a synthesized Error. The client HMR runtime is bundled by src/codegen/bake-codegen.ts:52-66 with target: "browser" but imports the Bun variant via src/runtime/bake/hmr-module.ts:9, so the dev server and the production build disagree. The base branch already imported __using and __callDispose from ../../runtime.bun, so this is pre-existing. Remedy: select the per-target __using/__callDispose when registering bun:wrap on the client, or add the fallback to runtime.bun.js.

Verification: Pre-existing and acknowledged in the PR description. A dev-server client page runs in a browser without a global SuppressedError, and a using/await using disposer throws. src/runtime.bun.js:19-23 defines __callDispose with new SuppressedError(e, error, ...) and no typeof SuppressedError === 'function' guard, unlike src/bundler/ParseTask.rs:533-535. The base already fails by the identical route, so merging makes nothing worse.

@rvion

rvion commented Oct 12, 2026

Copy link
Copy Markdown

@robobun will that get merged soon ? can this get into next version ?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TC39 decorators fail in the HMR runtime: missing bun:wrap helpers (source patch included)

2 participants